34장. 의존성은 왜 문제가 되는가
33장에서 우리는
레이어 아키텍처의 구조를 보았다.
- Controller → Service → Repository
- 위에서 아래로 의존하는 구조
이 구조는 단순하고 이해하기 쉽다.
하지만 시스템이 커질수록
문제가 발생한다.
의존성이란 무엇인가
의존성이란
한 코드가 다른 코드를 알고 사용하는 관계다.
예를 들어:
class OrderService {
private OrderRepository repository = new MySQLOrderRepository();
}
이 경우:
OrderService는 MySQLOrderRepository에 의존한다
문제는 의존성 자체가 아니다
의존성은 나쁜 것이 아니다.
문제는 이것이다.
무엇이 무엇에 의존하는가
변하는 것과 변하지 않는 것
시스템에는 두 가지 종류가 있다.
변하는 것
- DB (MySQL → DynamoDB)
- 메시지 브로커 (Kafka → SQS)
- 외부 API
- 캐시, 인프라 기술
이들은 상황에 따라
언제든 바뀔 수 있다.
변하지 않아야 하는 것
- 주문 생성 로직
- 결제 처리 규칙
- 상태 전이 규칙
이것은
서비스의 핵심 비즈니스 로직이다.
우리가 원하는 상태
자연스럽게 이렇게 생각할 수 있다.
변하지 않아야 하는 것은
변하는 것에 영향을 받으면 안 된다
하지만 현재 구조는 반대다
레이어 구조를 다시 보자.
Service → DB
Service → Kafka
Service → External API
이 구조의 의미는 이것이다.
비즈니스 로직이 외부 기술에 의존한다
왜 이것이 문제인가
이 상태에서는
변경이 발생할 때마다 문제가 생긴다.
1️⃣ 기술 변경이 로직을 흔든다
- DB 변경
- 메시지 시스템 변경
→ Service 코드 수정 필요
2️⃣ 테스트가 어려워진다
Service를 테스트하려면
- DB가 필요하고
- Kafka가 필요하다
→ 빠른 테스트 불가능
3️⃣ 코드가 점점 복잡해진다
Service
├─ DB 처리
├─ Kafka 발행
├─ 외부 API 호출
├─ 비즈니스 로직
→ 핵심 로직이 흐려진다
문제의 본질
핵심은 이것이다.
변하지 않아야 하는 것이
변하는 것에 의존하고 있다
이 구조에서는
시스템이 안정될 수 없다.
그래서 방향을 바꾼다
이 문제를 해결하려면
의존성 방향을 바꿔야 한다.
현재 구조
Service → DB
우리가 원하는 구조
DB → Service
즉,
외부 기술이 비즈니스 로직에 의존해야 한다
이게 가능한가?
여기서 자연스럽게 질문이 나온다.
DB가 Service를 어떻게 의존하지?
이 질문을 해결하는 것이
다음 개념들이다.
인터페이스
먼저, 역할을 정의한다.
interface OrderRepository {
void save(Order order);
}
Service는 이렇게 바뀐다.
class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
}
이제 Service는
구현체가 아니라 “역할”에 의존한다
중요한 변화
이 구조에서 핵심은 이것이다.
Service는 어떤 DB를 쓰는지 모른다
제어 역전 (Inversion of Control)
이제 객체를 누가 만들까?
기존:
Service가 직접 생성
변경 후:
외부에서 만들어서 넣어준다
이것을
제어 역전 (IoC)
이라고 한다.
의존성 주입 (Dependency Injection)
실제 연결은 이렇게 이루어진다.
OrderRepository repo = new MySQLOrderRepository();
OrderService service = new OrderService(repo);
이것을
의존성 주입 (DI)
이라고 한다.
결과적으로 바뀌는 것
Before
Service → MySQLRepository
After
Service → Repository Interface
Repository 구현체 → Service에 주입
이 변화의 의미
이 변화는 단순한 코드 스타일이 아니다.
1️⃣ 비즈니스 로직이 보호된다
Service는 더 이상
- DB
- Kafka
- 외부 API
를 알지 않는다
2️⃣ 기술 변경이 쉬워진다
- MySQL → DynamoDB 변경
→ Service 수정 없음
3️⃣ 테스트가 쉬워진다
OrderRepository fake = new FakeRepository();
OrderService service = new OrderService(fake);
→ 외부 의존 없이 테스트 가능
이벤트 기반 시스템에서의 의미
이벤트 기반 구조에서는
외부 의존성이 더 많다.
- 메시지 브로커
- Outbox
- Inbox
- 외부 API
이 상태에서 의존성 방향이 잘못되면
시스템은 빠르게 복잡해진다
이제 다음 단계
지금까지 우리는
- 의존성의 문제를 이해했고
- 방향을 왜 바꿔야 하는지 보았고
- 그 방법을 배웠다
이제 남은 질문은 이것이다.
이 구조를 시스템 전체에 적용하면 어떻게 될까?
그 답이
헥사고날 아키텍처
다.
이 장의 핵심
- 의존성 자체는 문제가 아니다
- 문제는 “의존성의 방향”이다
- 변하지 않아야 하는 것은 보호되어야 한다
- 외부 기술은 언제든 바뀔 수 있다
- 의존성 방향을 뒤집어야 구조가 안정된다
- 인터페이스, IoC, DI가 이를 가능하게 한다